import sys
from types import ModuleType

import pytest

import libqtile.config
from libqtile import widget


class MockMPD(ModuleType):
    class ConnectionError(Exception):
        pass

    class CommandError(Exception):
        pass

    class MPDClient:
        tracks = [
            {"title": "Never gonna give you up", "artist": "Rick Astley", "song": "0"},
            {"title": "Sweet Caroline", "artist": "Neil Diamond"},
            {"title": "Marea", "artist": "Fred Again.."},
            {},
            {"title": "Sweden", "performer": "C418"},
        ]

        def __init__(self):
            self._index = 0
            self._connected = False
            self._state_override = True
            self._status = {"state": "pause"}

        @property
        def _current_song(self):
            return self.tracks[self._index]

        def ping(self):
            if not self._connected:
                raise ConnectionError()
            return self._state_override

        def connect(self, host, port):
            return True

        def command_list_ok_begin(self):
            pass

        def status(self):
            return self._status

        def currentsong(self):
            return self._index + 1

        def command_list_end(self):
            return (self.status(), self._current_song)

        def close(self):
            pass

        def disconnect(self):
            pass

        def pause(self):
            self._status["state"] = "pause"

        def play(self):
            self._status["state"] = "play"

        def stop(self):
            self._status["state"] = "stop"

        def next(self):
            self._index = (self._index + 1) % len(self.tracks)

        def previous(self):
            self._index = (self._index - 1) % len(self.tracks)

        def add_states(self):
            self._status.update(
                {"repeat": "1", "random": "1", "single": "1", "consume": "1", "updating_db": "1"}
            )

        def force_idle(self):
            self._status["state"] = "stop"
            self._index = 3


@pytest.fixture
def mpd2_manager(manager_nospawn, monkeypatch, minimal_conf_noscreen, request):
    monkeypatch.setitem(sys.modules, "mpd", MockMPD("mpd"))
    monkeypatch.setattr("libqtile.widget.mpd2widget.MPDClient", MockMPD.MPDClient)

    config = minimal_conf_noscreen
    config.screens = [
        libqtile.config.Screen(
            top=libqtile.bar.Bar(
                [widget.Mpd2(**getattr(request, "param", dict()))],
                50,
            ),
        )
    ]

    manager_nospawn.start(config)
    yield manager_nospawn


def test_mpd2_widget_display_and_actions(mpd2_manager):
    widget = mpd2_manager.c.widget["mpd2"]
    assert widget.info()["text"] == "⏸ Rick Astley/Never gonna give you up [-----]"

    # Button 1 toggles state
    mpd2_manager.c.bar["top"].fake_button_press(0, 0, 1)
    widget.eval("self.update(self.poll())")
    assert widget.info()["text"] == "▶ Rick Astley/Never gonna give you up [-----]"

    # Button 3 stops
    mpd2_manager.c.bar["top"].fake_button_press(0, 0, 3)
    widget.eval("self.update(self.poll())")
    assert widget.info()["text"] == "■ Rick Astley/Never gonna give you up [-----]"

    # Button 1 toggles state
    mpd2_manager.c.bar["top"].fake_button_press(0, 0, 1)
    widget.eval("self.update(self.poll())")
    assert widget.info()["text"] == "▶ Rick Astley/Never gonna give you up [-----]"

    mpd2_manager.c.bar["top"].fake_button_press(0, 0, 1)
    widget.eval("self.update(self.poll())")
    assert widget.info()["text"] == "⏸ Rick Astley/Never gonna give you up [-----]"

    # Button 5 is "next"
    mpd2_manager.c.bar["top"].fake_button_press(0, 0, 5)
    widget.eval("self.update(self.poll())")
    assert widget.info()["text"] == "⏸ Neil Diamond/Sweet Caroline [-----]"

    mpd2_manager.c.bar["top"].fake_button_press(0, 0, 5)
    widget.eval("self.update(self.poll())")
    assert widget.info()["text"] == "⏸ Fred Again../Marea [-----]"

    # Button 4 is previous
    mpd2_manager.c.bar["top"].fake_button_press(0, 0, 4)
    widget.eval("self.update(self.poll())")
    assert widget.info()["text"] == "⏸ Neil Diamond/Sweet Caroline [-----]"

    mpd2_manager.c.bar["top"].fake_button_press(0, 0, 4)
    widget.eval("self.update(self.poll())")
    assert widget.info()["text"] == "⏸ Rick Astley/Never gonna give you up [-----]"


def test_mpd2_widget_extra_info(mpd2_manager):
    """Quick test to check extra info is displayed ok."""
    widget = mpd2_manager.c.widget["mpd2"]

    # Inject everything to make test quicker
    widget.eval("self.client.add_states()")

    # Update widget and check text
    widget.eval("self.update(self.poll())")
    assert widget.info()["text"] == "⏸ Rick Astley/Never gonna give you up [rz1cU]"


def test_mpd2_widget_idle_message(mpd2_manager):
    """Quick test to check idle message."""
    widget = mpd2_manager.c.widget["mpd2"]

    # Inject everything to make test quicker
    widget.eval("self.client.force_idle()")

    # Update widget and check text
    widget.eval("self.update(self.poll())")
    assert widget.info()["text"] == "■ MPD IDLE[-----]"


@pytest.mark.parametrize(
    "mpd2_manager", [{"status_format": "{currentsong}: {artist}/{title}"}], indirect=True
)
def test_mpd2_widget_current_song(mpd2_manager):
    """Quick test to check currentsong info"""
    widget = mpd2_manager.c.widget["mpd2"]
    assert widget.info()["text"] == "1: Rick Astley/Never gonna give you up"


@pytest.mark.parametrize(
    "mpd2_manager",
    [{"undefined_value": "Unknown", "status_format": "{title} ({year})"}],
    indirect=True,
)
def test_mpd2_widget_custom_undefined_value(mpd2_manager):
    """Quick test to check undefined_value option"""
    widget = mpd2_manager.c.widget["mpd2"]
    assert widget.info()["text"] == "Never gonna give you up (Unknown)"


def test_mpd2_widget_dynamic_artist_value(mpd2_manager):
    """Quick test to check dynamic artist value"""
    widget = mpd2_manager.c.widget["mpd2"]
    widget.eval("self.client._index = 4")
    widget.eval("self.update(self.poll())")
    assert widget.info()["text"] == "⏸ C418/Sweden [-----]"
